import ServerPackge.*;
import org.dom4j.*;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Minicat 启动类
 */
public class Bootstrap {
    private int port = 8081;
    String appBase = "";

    private Mapper mapper = new Mapper();

    /**
     * Minicat 5.0
     */
    private final Map<String, Mapper> servletMap2 = new HashMap<>();

    private final Map<String, HttpServlet> servletMap = new HashMap<>();

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }


    public void start() throws IOException {
        // 加载解析相关配置
//        loadServlet();
        String portStr = loadServer();
        port = Integer.parseInt(portStr);
//        loadAppBaseServlet();

        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("======>>Minicat start on port:" + port);
        startThread2(serverSocket);
    }

    private void loadAppBaseServlet() {
        File file = new File(this.appBase);
        if(null != file.listFiles()){
            for (File fileItem : Objects.requireNonNull(file.listFiles())) {
                if(fileItem.isDirectory()){
                    String webXmlPath = fileItem.getAbsolutePath() + "/web.xml";
                    File webXml = new File(webXmlPath);
                    if(webXml.exists()){

                        try {
                            FileInputStream inputStream = new FileInputStream(webXmlPath);
                            Map<String, HttpServlet> tempServletMap = new HashMap<>();
                            // 加载配置文件
                            loadServletByInputStream(inputStream, tempServletMap);
                            // 动态资源
                            this.mapper.putServletMap(fileItem.getName(),tempServletMap);
                            // 静态资源
                            this.mapper.putResourcesMap(fileItem.getName(), fileItem.getAbsolutePath());
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }else{
                    System.out.println("缺失web.xml配置");
                }

            }
        }


    }

    /**
     * 加载 server.xml
     */
    @SuppressWarnings({"unchecked"})
    private String loadServer() {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        String portStr="";
        try {
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();

            // 1. 解析Server标签
            Element serverElement = (Element) rootElement.selectSingleNode("//Server");

            // 2. 解析Server下的Service标签
            List<Element> serverNodes = serverElement.selectNodes("//Services");

            // 3. Service下的Hosts存储
            HashMap<String, Host> hostHashMap = new HashMap<>();

            for (Element serviceItem : serverNodes) {
                String serviceName = serviceItem.attributeValue("name");

                Element portNode = (Element) serviceItem.selectSingleNode("//Connector");
                portStr = portNode.attributeValue("port");

                Element engineNode = (Element) serviceItem.selectSingleNode("//Engine");
                // 存储的项目集合
                List<Element> hostNodes = engineNode.selectNodes("//Host");

                HashMap<String, Context> contextMap = new HashMap<>();
                for (Element hostNode : hostNodes) {
                    String hostName = hostNode.attributeValue("name");
                    String appBase = hostNode.attributeValue("appBase");
                    File file = new File(appBase);
                    if (!file.exists() || file.list() == null) {
                        // 文件不存在或目录下为空不做处理
                        break;
                    }
                    String[] fileList = file.list();

                    for (String filePath : fileList) {
                        // 遍历所有项目，创建并实例化servlet，存入对应路径
                        contextMap.put(filePath,loadContextServlet(appBase + "/" + filePath));
                    }

                    // host:port
                    hostHashMap.put(hostName+":"+ portStr,new Host(contextMap));
                }
                servletMap2.put(serviceName , new Mapper(hostHashMap));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return portStr;
    }

    /**
     * 功能描述: <br>
     * <pre>
     *     加载实际项目中的Servlet
     *     实际从传入项目路径下的web.xml读取servlet配置信息
     * </pre>
     *
     * @param path 项目路径
     * @return ServerPackge.Context
     * @since 1.0
     * @author isaac
     * @date 2020/11/3 20:33
     */
    @SuppressWarnings({"unchecked"})
    private Context loadContextServlet(String path) throws Exception {
        String webPath = path + "/web.xml";
        // 文件存在性判断
        if (!new File(webPath).exists()) {
            System.out.println("NOT FOUND web.xml FILE");
            return null;
        }

        // 读取 web.xml 配置文件，读取servlet配置
        FileInputStream fileInputStream = new FileInputStream(webPath);
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(fileInputStream);

        Element rootElement = document.getRootElement();
        List<Element> selectNodes = rootElement.selectNodes("//servlet");

        HashMap<String, HttpServlet> servletMap = new HashMap<>();

        // 遍历所有的节点信息
        for (Element selectNode : selectNodes) {
            // <servlet-name>Isaac</servlet-name>
            Element servletNameElement = (Element) selectNode.selectSingleNode("servlet-name");
            String servletName = servletNameElement.getStringValue();

            // <servlet-class>ServerPackge.IsaacServlet</servlet-class>
            Element servletClassElement = (Element) selectNode.selectSingleNode("servlet-class");
            String servletClass = servletClassElement.getStringValue();

            // 根据 servlet-name 找到 url-pattern
            Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
            // <url-pattern>/isaac</url-pattern>
            String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

            // 获取构造器
            servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).getDeclaredConstructor().newInstance());
        }

        return new Context(servletMap);
    }


    /**
     * 加载解析web.xml，初始化servlet
     */
    private void loadServlet() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        loadServletByInputStream(resourceAsStream,servletMap);
    }


    private void loadServletByInputStream(InputStream inputStream,Map<String,HttpServlet> map){
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();
            List<Element> selectNodes = rootElement.selectNodes("//servlet");

            for (Element selectNode : selectNodes) {
                //  <servlet-name>Isaac</servlet-name>
                Element servletNameElement = (Element) selectNode.selectSingleNode("servlet-name");
                String servletName = servletNameElement.getStringValue();
                //  <servlet-class>Server.IsaacServlet</servlet-class>
                Element servletClassElement = (Element) selectNode.selectSingleNode("servlet-class");
                String servletClass = servletClassElement.getStringValue();

                // 根据servlet-name 值找到 url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                // isaac
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                // urlPattern , ServletClass
                map.put(urlPattern, (HttpServlet) Class.forName(servletClass).getDeclaredConstructor().newInstance());

            }

        } catch (DocumentException | IllegalAccessException | InstantiationException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * <pre>
     *     V1.0需求：浏览器请求 http://localhost:8080, 返回固定字符串到页面 "Hello Minicat"
     * </pre>
     *
     * @param serverSocket
     */
    private void startV1(ServerSocket serverSocket) throws IOException {
        while(true){
            Socket socket = serverSocket.accept();
            OutputStream outputStream = socket.getOutputStream();
            String data = "Hello Minicat";
            String responseText = HttpProtocolUtil.getHttpHeader200(data.getBytes().length) + data;
            outputStream.write(responseText.getBytes());
            socket.close();
        }
    }

    /**
     * <pre>
     *     V2.0需求:封装 Request 和 Response 对象，返回 html静态文件
     * </pre>
     * @param serverSocket
     */
    private void startV2(ServerSocket serverSocket) throws IOException {
        while(true){
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            // 封装Request对象和Response对象
            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());

            response.outputHtml(request.getUrl());
            socket.close();
        }
    }

    /**
     * <pre>
     *     V3.0需求：可以请求动态资源（Servlet）
     * </pre>
     * @param serverSocket
     * @throws IOException
     */
    private void startV3(ServerSocket serverSocket) throws IOException {
        while(true){
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();

            // 封装Request对象和Response对象
            Request request = new Request(inputStream);
            Response response = new Response(socket.getOutputStream());

            // 静态资源处理
            if(servletMap.get(request.getUrl()) == null){
                response.outputHtml(request.getUrl());
            }else{
                // 动态资源请求
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request,response);
            }
        }
    }

    /**
     * 多线程改造（不使用线程池）
     * @param serverSocket
     * @throws IOException
     */
    private void startThread(ServerSocket serverSocket) throws IOException {
        while(true){
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
            requestProcessor.start();
        }

    }


    private void startThread2(ServerSocket serverSocket) throws IOException {
        while(true){
            Socket socket = serverSocket.accept();
            RequestProcessor2 requestProcessor2 = new RequestProcessor2(socket, new Server(servletMap2));
            requestProcessor2.start();
        }
    }



    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
